﻿namespace Athena.Infrastructure.Summaries;

/// <summary>
/// 
/// </summary>
public static class XmlCommentsNodeNameHelper
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="method"></param>
    /// <returns></returns>
    public static string GetMemberNameForMethod(MethodInfo method)
    {
        var builder = new StringBuilder("M:");

        builder.Append(QualifiedNameFor(method.DeclaringType));
        builder.Append($".{method.Name}");

        var parameters = method.GetParameters();
        if (!parameters.Any())
        {
            return builder.ToString();
        }

        var parametersNames = parameters.Select(p =>
            p.ParameterType.IsGenericParameter
                ? $"`{p.ParameterType.GenericParameterPosition}"
                : QualifiedNameFor(p.ParameterType, true)
        );
        builder.Append($"({string.Join(",", parametersNames)})");

        return builder.ToString();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static string GetMemberNameForType(Type? type)
    {
        var builder = new StringBuilder("T:");
        builder.Append(QualifiedNameFor(type));

        return builder.ToString();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="fieldOrPropertyInfo"></param>
    /// <returns></returns>
    public static string GetMemberNameForFieldOrProperty(MemberInfo fieldOrPropertyInfo)
    {
        var builder = new StringBuilder(((fieldOrPropertyInfo.MemberType & MemberTypes.Field) != 0) ? "F:" : "P:");
        builder.Append(QualifiedNameFor(fieldOrPropertyInfo.DeclaringType));
        builder.Append($".{fieldOrPropertyInfo.Name}");

        return builder.ToString();
    }

    private static string QualifiedNameFor(Type? type, bool expandGenericArgs = false)
    {
        if (type!.IsArray)
            return $"{QualifiedNameFor(type.GetElementType(), expandGenericArgs)}[]";

        var builder = new StringBuilder();

        if (!string.IsNullOrEmpty(type.Namespace))
            builder.Append($"{type.Namespace}.");

        if (type.IsNested)
        {
            builder.Append($"{string.Join(".", GetNestedTypeNames(type))}.");
        }

        if (type.IsConstructedGenericType && expandGenericArgs)
        {
            var nameSansGenericArgs = type.Name.Split('`').First();
            builder.Append(nameSansGenericArgs);

            var genericArgsNames = type.GetGenericArguments().Select(t => t.IsGenericParameter
                ? $"`{t.GenericParameterPosition}"
                : QualifiedNameFor(t, true));

            builder.Append($"{{{string.Join(",", genericArgsNames)}}}");
        }
        else
        {
            builder.Append(type.Name);
        }

        return builder.ToString();
    }

    private static IEnumerable<string?> GetNestedTypeNames(Type? type)
    {
        if (type != null && (!type.IsNested || type.DeclaringType == null)) yield break;

        foreach (var nestedTypeName in GetNestedTypeNames(type?.DeclaringType))
        {
            yield return nestedTypeName;
        }

        yield return type?.DeclaringType?.Name;
    }
}